﻿//: \$tango libyaml.obj -lib

module yamld ;

/*******************************************************}
{                                                       }
{       Yet Another YAML Ain't Markup Language          }
{       D Import Header                                 }
{                                                       }
{       2004 YT (demoonlit@inter7.jp)                   }
{                                                       }
{*******************************************************/

private import
	tango.stdc.string,
	tango.stdc.stringz,
	Flo	= tango.text.convert.Float ,
	Int	= tango.text.convert.Integer,
	Utf	= tango.text.convert.Utf ;

private{
	alias bool bit;
	int icmp(char[] s1, char[] s2){
		auto len = s1.length;
		int result ;
		if (s2.length < len)
			len = s2.length;
		for (size_t i = 0; i < len; i++){
			if (s1[i] != s2[i])
			{
			char c1 = s1[i];
			char c2 = s2[i];

			if (c1 >= 'A' && c1 <= 'Z')
				c1 += cast(int)'a' - cast(int)'A';
			if (c2 >= 'A' && c2 <= 'Z')
				c2 += cast(int)'a' - cast(int)'A';
			result = cast(int)c1 - cast(int)c2;
			if (result)
				break;
			}
		}
		if (result == 0)
			result = cast(int) s1.length - cast(int) s2.length;
		return result ;
	}

extern(C){
	enum Yaml_Token_Kind {
		error,
		end,
		enter_mapping,
		enter_sequence,
		leave,
		value,
	}
	alias Yaml_Token_Kind ytk;
	
	align(4) struct Yaml_Token_Struct {
		Yaml_Token_Kind kind;
		uint name_dummy;
		char[] name;
		uint value_dummy;
		char[] value;
		uint comment_dummy;
		char[] comment;
	}
	
	align(4) struct Reader_Struct {
		Yaml_Token_Struct next;
		int line;
		uint indents_capacity;
		uint indents_length;
		int* indents;
		uint source_length;
		char* source;
		uint state;
		void* heap;
	}
	
	align(4) struct Writer_Struct {
		uint output_capacity;
		uint output_dummy;
		char[] output;
		int line;
		int indents_capacity;
		int indents_length;
		Yaml_Token_Kind* indents;
		uint indent_string_length;
		char* indent_string;
		uint line_string_length;
		char* line_string;
		uint state;
		void* heap;
	}
	
	alias void function(char c, void* context) putc_t;

	bit yaml_reader_initialize(Reader_Struct* s, char[] source, uint* type_name_length, char** type_name);
	void yaml_reader_finalize(Reader_Struct* s);
	bit yaml_reader_advance(Reader_Struct* s);
	bit yaml_reader_advance_structure(Reader_Struct* s);
	
	bit yaml_writer_initialize(Writer_Struct* s, char[] indent, char[] line, char[] type);
	void yaml_writer_finalize(Writer_Struct* s);
	bit yaml_writer_append(Writer_Struct* s, Yaml_Token_Struct *token);
	
	void yaml_string_escape(char* s, int length, putc_t putc, void* context);
	void yaml_string_extract_escape(char* s, int length, putc_t putc, void* context);
	
	void add_char(char c, void* context)
	{
		*(cast(char[]*)context) ~= c;
	}
}}

char[] yaml_escape(char[] s)
{
	char[] result = ``;
	yaml_string_escape(s.ptr, s.length, &add_char, &result);
	return result;
}

char[] yaml_extract_escape(char[] s)
{
	char[] result = ``;
	yaml_string_extract_escape(s.ptr, s.length, &add_char, &result);
	return result;
}

bit toBit(char[] s)
{
	return (s.length != 0) && (icmp(s, "false") != 0) && (icmp(s, "no") != 0) && (s != "0");
}

class Yaml_Error : Exception
{
	this(char[] message) { super(message); }
}

alias void delegate() Yaml_IO_Hash;
alias void delegate(int i) Yaml_IO_List;

struct Dynamic_Array 
{
	void* value;
	int function(void* value) get_length;
	void function(void* value, int len) set_length;
}

template dynamic_array(_Array)
{
	Dynamic_Array wrap(_Array* value)
	{
		Dynamic_Array result;
		result.value = value;
		result.get_length = function int(void* value){ return (*cast(_Array*)value).length; };
		result.set_length = function void(void* value, int len){ (*cast(_Array*)value).length = len; };
		return result;
	}
}

interface Yaml_IO {
	void io(in char[] name, inout char[] value); 
	void io(in char[] name, inout dchar value);
	void io(in char[] name, inout int value); 
	void io(in char[] name, inout uint value); 
	void io(in char[] name, inout bit value); 
	void io(in char[] name, inout ubyte value); 
	void io(in char[] name, Dynamic_Array value, in Yaml_IO_List proc); 
	void io(inout char[] value);
	void io(inout int value); 
	void io(inout bit value); 
	void io(Yaml_IO_Hash proc);
}

final class Yaml_Reader : Yaml_IO 
{
	this(char[] _type_name, char[] _source)
	{
		uint type_name_length;
		char* type_name;
		source_field = _source;
		if(!yaml_reader_initialize(&s, source_field, &type_name_length, &type_name)){
			throw new Yaml_Error(`YAML Initialization Error`);
		}
		if(_type_name.length > 0 && _type_name != type_name[0..type_name_length]){
			throw new Yaml_Error(`YAML Type Error`);
		}
	}

	~this()
	{
		yaml_reader_finalize(&s);
	}
	
	void advance()
	{
		if(!yaml_reader_advance(&s)){
			throw new Yaml_Error(`YAML Error`);
		}
	}
	
	void advance_structure()
	{
		if(!yaml_reader_advance_structure(&s)){
			throw new Yaml_Error(`YAML Structure Error`);
		}
	}
	
	Yaml_Token_Struct* next(){ return &s.next; }
	char[] next_name(){ return s.next.name; }
	char[] next_value(){ return s.next.value; }
	char[] next_string(){ return yaml_extract_escape(s.next.value); }
	char[] next_comment(){ return s.next.comment; }
	
	void io(in char[] name, inout char[] value)
	{
		if(name == next_name){
			value = next_string;
		}
	}
	
	
	void io(in char[] name, inout dchar value)
	{
		if(name == next_name) {
			char[] u		= next_string ;
			uint index	= 0;
			value			= Utf.decode( next_string , index );
		}
	}
	
	void io(in char[] name, inout int value) {
		if(name == next_name){
			value = Int.toInt(next_value);
		}
	}
	
	void io(in char[] name, inout uint value)
	{
		if(name == next_name){
			value		= Int.toLong(next_value);
		}
	}
	
	void io(in char[] name, inout bit value)
	{
		if(name == next_name){
			value = toBit(next_value);
		}
	}
	
	void io(in char[] name, inout ubyte value)
	{
		if(name == next_name){
			value = Int.toInt(next_value);
		}
	}

	void io(in char[] name, Dynamic_Array value, in Yaml_IO_List proc)
	{
		if(name == next_name && s.next.kind == ytk.enter_sequence){
			advance();
			value.set_length(value.value, 0);
			int i = 0;
			int len = 0;
			while(s.next.kind != ytk.leave){
				++len;
				value.set_length(value.value, len);
				proc(i);
				++i;
				advance_structure();
			}
		}
	}

	void io(inout char[] value)
	{
		value = next_string;
	}

	void io(inout int value)
	{
		value = Int.toInt(next_value);
	}
	
	void io(inout bit value)
	{
		value = toBit(next_value);
	}
	
	void io(Yaml_IO_Hash proc)
	{
		if(s.next.kind == ytk.enter_mapping){
			advance();
			while(s.next.kind != ytk.leave){
				proc();
				advance_structure();
			}
		}
	}
private:
	Reader_Struct s;
	char[] source_field;
}

final class Yaml_Writer : Yaml_IO
{
	this(char[] _type_name){
		version(Win32){
			const char[] line = "\r\n";
		}else version(Mac){
			const char[] line = "\r";
		}else{
			const char[] line = "\n";
		}
		const char[] indent = "\t";
		if(!yaml_writer_initialize(&s, indent, line, _type_name)){
			throw new Yaml_Error(`YAML Writer Initialization Error`);
		}
	}

	~this(){
		yaml_writer_finalize(&s);
	}
	
	void write(in Yaml_Token_Struct token)
	{
		if(!yaml_writer_append(&s, &token)){
			throw new Yaml_Error(`YAML Output Error`);
		}
	}
	
	void write(char[] name, char[] value)
	{
		Yaml_Token_Struct t;
		t.kind		= ytk.value;
		t.name	= name;
		t.value	= value;
		write(t);
	}
	
	void enter_list(char[] name)
	{
		Yaml_Token_Struct t;
		t.kind = ytk.enter_sequence;
		t.name = name;
		write(t);
	}
	
	void enter_hash(char[] name)
	{
		Yaml_Token_Struct t;
		t.kind = ytk.enter_mapping;
		t.name = name;
		write(t);
	}
	
	void leave()
	{
		Yaml_Token_Struct t;
		t.kind = ytk.leave;
		write(t);
	}
	
	char[] output()
	{
		return s.output.dup;
	}
	
	void io(in char[] name, inout char[] value)
	{ 
		write(name, yaml_escape(value));
	}
	
	
	void io(in char[] name, inout dchar value)
	{ 
		char[] s;
		s	= Utf.encode(s, value);
		write(name, yaml_escape(s));
	}
	
	void io(in char[] name, inout int value)
	{ 
		write(name, Int.toString(value));
	}
	
	void io(in char[] name, inout uint value)
	{ 
		write(name, Int.toString(value));
	}
	
	void io(in char[] name, inout bit value)
	{ 
		write(name, value ? `true` : `false`);
	}
	
	void io(in char[] name, inout ubyte value)
	{ 
		write(name, Int.toString(value));
	}
	
	void io(in char[] name, Dynamic_Array value, in Yaml_IO_List proc)
	{
		enter_list(name);
		int len = value.get_length(value.value);
		for(int i = 0; i < len; ++i){
			proc(i);
		}
		leave();
	}
	
	void io(inout char[] value)
	{
		 write(``, yaml_escape(value));
	}
	
	void io(inout int value)
	{ 
		write(``, Int.toString(value));
	}
	
	void io(inout bit value)
	{ 
		write(``, value ? `true` : `false`);
	}
	
	void io(Yaml_IO_Hash proc)
	{ 
		enter_hash(``);
		proc();
		leave();
	}
private:
	Writer_Struct s;
}

abstract class Yaml_Element
{
	this(){}
	bit is_hash() { return false; }
	bit is_list() { return false; }
	bit is_scalar() { return false; }
	char[] value(){ assert(false); return ``; }
	char[] string_value(){ assert(false); return ``; }
	int integer_value() { assert(false); return 0; }
	real real_value() { assert(false); return 0.0; }
	bit bit_value() { assert(false); return false; }
	Yaml_Element[] list() { assert(false); return null; }
	Yaml_Element[char[]] hash() { assert(false); return null; }
	//operaters
	Yaml_Element opIndex(int index) { Yaml_Element[] l = list(); return l[index]; }
	Yaml_Element opIndex(char[] name) { Yaml_Element[char[]] h = hash(); return h[name]; }
	//(bogoYAML互換)
	alias is_hash isAssoc;
	alias is_list isArray;
	alias is_scalar isScalar;
	alias string_value str;
	alias integer_value integer;
	alias real_value decimal;
	alias list array;
	alias hash assoc;
}

final class Yaml_Hash : Yaml_Element
{
	this(Yaml_Element[char[]] _value, char[][] _keys)
	{
		super();
		value_field = _value;
		keys_field = _keys;
	}
	override bit is_hash() { return true; }
	override Yaml_Element[char[]] hash() { return value_field; }
	bit keys_valid(){ return keys_field.length == value_field.length; }
	char[][] keys(){ return keys_field; }
private:
	Yaml_Element[char[]] value_field;
	char[][] keys_field;
}

final class Yaml_List : Yaml_Element
{
	this(Yaml_Element[] _value)
	{
		super();
		value_field = _value;
	}
	override bit is_list() { return true; }
	override Yaml_Element[] list() { return value_field; }
private:
	Yaml_Element[] value_field;
}

final class Yaml_Scalar : Yaml_Element
{
	this(char[] _value)
	{
		super();
		value_field = _value;
	}
	override bit is_scalar() { return true; }
	override char[] value(){ return value_field; }
	override char[] string_value(){ return yaml_extract_escape(value_field); }
	override int integer_value() { return Int.toInt(value_field); }
	override real real_value() { return Flo.toFloat(value_field); }
	override bit bit_value() { return toBit(value_field); }
private:
	char[] value_field;
}

Yaml_Element yaml_load(char[] _type_name, char[] _source)
{
	auto Yaml_Reader reader = new Yaml_Reader(_type_name, _source);
	
	Yaml_Element parse_internal()
	{
		Yaml_Element result;
		switch(reader.next.kind){
		case ytk.enter_mapping:
			Yaml_Element[char[]] hash;
			char[][] keys;
			reader.advance();
			while(reader.next.kind != ytk.leave){
				char[] name = reader.next_name();
				hash[name] = parse_internal();
				keys ~= name;
			}
			reader.advance();
			result = new Yaml_Hash(hash, keys);
			break;
		case ytk.enter_sequence:
			Yaml_Element[] list;
			reader.advance();
			while(reader.next.kind != ytk.leave){
				list ~= parse_internal();
			}
			reader.advance();
			result = new Yaml_List(list);
			break;
		case ytk.value:
			result = new Yaml_Scalar(reader.next_value());
			reader.advance();
			break;
		}
		assert(result);
		return result;
	}
	
	return parse_internal();
}

//(bogoYAML互換)
alias Yaml_Error YamlError;
alias Yaml_Element YamlHolder;
alias Yaml_Hash YamlAssoc;
alias Yaml_List YamlArray;
alias Yaml_Scalar YamlScalar;
alias yaml_load yamlLoad;
